/**********************
**intellistrip.c
**Date Created: 4/18/2009
**Copyright 2009 Tomorrow Tech Industries
**Contact: RPITomorrowTech@gmail.com
**********************/


//-----------------------------------------------------------------------------


// include files. This file is available online
#include <c8051_SDCC.h>
#include <stdio.h>
#include <stdlib.h>
#include "../shared/intellistrip.h"


//-----------------------------------------------------------------------------
// Function PROTOTYPES
//-----------------------------------------------------------------------------
// Initialize ports for input and output
void Port_Init(void);
// Initialize Timer 0
void Timer_Init();

//-----------------------------------------------------------------------------
// Global Variables
//-----------------------------------------------------------------------------



/*
Make volatile any numbers accessed in ISR
*/
//How many overflows have occured
volatile unsigned int g_counts = 0;
//What to overflow counter at
volatile int g_timer_start = 0;
//Local copy of our port data
int g_portShadow = 0;

#define TIMER_START				0xF0

#define DEBUG_PORT				P2
#define DEBUG_PORT_MDOUT		P2MDOUT
#define STRIP_PORT				P3
#define STRIP_PORT_MDOUT		P3MDOUT

//***************
void Port_Init(void)
{
	//Output ports
	STRIP_PORT_MDOUT |= 0xFF;
	DEBUG_PORT_MDOUT |= 0xFF;

	// Port 2: input port
	//P0MDOUT = 0x00;
	//PORT_READ = 0xFF;
}

//***************
void Timer_Init(void)
{
	//pg 226
	CKCON |= 0x04; // Timer0 uses SYSCLK as source
	CKCON |= 0x40; // Timer0 does not divide sysclock by 12
	//pg 232
	TMOD &= 0xF0; // Clear the 4 least significant bits
	TMOD = TMOD & 0xFC + 0x01; // Make T1 intact and T0 use mode 1
	TR0 = 0;// Stop Timer0
	TL0 = 0; // Clear low byte of register T0
	TH0 = 0;// Clear high byte of register T0
}

//***************
void Timer0_ISR(void) interrupt 1
{
	TH0 = g_timer_start >> 8;
	TL0 = g_timer_start & 0xFF;
	++g_counts; // increment overflow counter
}

void putchar_pause(void)
{
	//Buzz for 1/4 second
	g_counts = 0;
	// Timer 0 enabled
	TR0 = 1;
	//Wait for our overflow: 22.1184 cycles / sec / (8192 * 8) cycles = 338
	while( g_counts < (338 / 10) )
	{
	}
	TR0 = 0;// Timer 0 disabled
}

/*
void print_padded_hex_byte(unsigned char byte)
{
	int byte_int = byte;
	if( byte < 0x10 )
	{
		printf("0%X", byte_int);
	}
	else
	{
		printf("%X", byte_int);
	}
	//print_pause();
}
*/

//Really hackish...
//Links in the putchar function for printf
//Interestingly enough, if we make this inline, it will fail
void nothing()
{
	putchar(' ');
}

//Only one of these is used at a time
//recieveBuff is more or less used just to set the size of this entire memory area
struct DataBuffer
{
	char recieveBuff[MAX_REQUEST__SIZE];
	struct IntellistripSerialRequest g_requestReal;
	struct IntellistripSerialResponse g_responseReal;
};

//The instance of our buffer
struct DataBuffer g_recieveBuffer;
struct IntellistripSerialRequest *g_request = &g_recieveBuffer.g_requestReal;
struct IntellistripSerialResponse *g_response = &g_recieveBuffer.g_responseReal;

void sendResponse()
{
	int i;

//DEBUG_PORT = 0x04;
//putchar(0x04);
//putchar_pause();

	//Packet size
	putchar(g_response->header.size);
	//Checksum
	putchar(g_response->header.checksum);

	//TT_* return code
	putchar(g_response->rc);
	for( i = 0; i < g_response->header.size; ++i )
	{
		putchar(g_response->additional[i]);
		putchar_pause();
	}

//DEBUG_PORT = 0x08;
//putchar(0x08);
//putchar_pause();
}

void processRequest()
{
	ttUI8_t rc = TT_GENERAL;

	switch( g_request->request )
	{
	case REQUEST__GET_PORT_POWER:
		{
			int port = g_request->additional[REQUEST__GET_PORT_POWER__PORT];
			int portPower;


DEBUG_PORT = 0x01;

			//Indexing starts at 1
			if( port == 0 )
			{
				goto error;
			}
			//Convert to mask
			port = 1 << port;
			//See if the bit was set
			portPower = port & g_portShadow;
			if( portPower )
			{
				//100%
				portPower = 100;
			}
			g_response->additional[RESPONSE__GET_PORT_POWER__LEVEL] = portPower;
			//No response
			g_response->header.size = 1;
		}
		break;

	case REQUEST__SET_PORT_POWER:
		{
			int port = g_request->additional[REQUEST__SET_PORT_POWER__PORT];
			int level = g_request->additional[REQUEST__SET_PORT_POWER__LEVEL];

DEBUG_PORT = 0x02;
//putchar(0x02);
//putchar_pause();
			if( port < 1 || port > 5 )
			{
				goto error;
			}

			//Convert to mask
			port = 1 << (port - 1);

			//On?
			if( level > 0 )
			{
				//Set the bit
				g_portShadow |= port;
			}
			//Off
			else
			{
				//Clear the bit
				g_portShadow &= 0xFF ^ port;
			}
			STRIP_PORT = g_portShadow;
			//No real response
			g_response->header.size = 0;
		}
		break;

	case REQUEST__GET_INFORMATION:
		{
DEBUG_PORT = 0x03;
			//Unsupported for now
			rc = TT_UNSUPPORTED;
			goto error;
		}
		break;

	//Ignore unknown requests
	default:
		break;
	}

	rc = TT_OK;

error:
	//Return code goes back to the master serial endpoint
	g_response->rc = rc;
}

void refresh_watchdog()
{
	//Initialize/reset watchdog timer
	WDTCN = 0xA5;
}

//***************
void main(void)
{
	// System Initialization
	Sys_Init();
	// Initialize ports 2 and 3
	Port_Init();
	// Initialize Timer 0
	Timer_Init();
	//pg 119
	// enable Timer0 Interrupt request
	IE |= 0x02;
	//This must be a bit, its part of the IE SFR
	// enable global interrupts
	EA = 1;

	//This LED is on the board, can be used for debugging
	STRIP_PORT = 0x00;
	//Initial
	DEBUG_PORT = 0x01;

	g_portShadow = 0x00;

	refresh_watchdog();

	for( ;; )
	{
		char c = getchar();
		int port;

		port = c & 0x0F;

		//g_portShadow |= 0x02;

		//On
		if( c & 0x80 )
		{
			//Set the bit
			g_portShadow |= port;
		}
		//off
		else
		{
			//Clear the bit
			g_portShadow &= 0xFF ^ port;
		}

		//++g_portShadow;

		STRIP_PORT = g_portShadow;
		//If we are still looping, we haven't frozen yet and should update the watchdog timer
		refresh_watchdog
	}

#if 0
	//Loop, recieving commands
	for( ;; )
	{
		int recieveIndex;

		/*****
		Header
		*****/
		//First byte is the size
		g_request->header.size = getchar();
		//Second byte is the checksum
		g_request->header.checksum = getchar();

		/*****
		Request
		*****/
		//Request
		g_request->request = getchar();


		//Payload
		//Burn through chars and drop request if needed so as to keep serial synchronization
		if( g_request->header.size > MAX_REQUEST__PAYLOAD )
		{
			for( recieveIndex = 0; recieveIndex < g_request->header.size; ++recieveIndex )
			{
				//Drop the data
				getchar();
			}
		}
		else
		{
			//Get the payload
			for( recieveIndex = 0; recieveIndex < g_request->header.size; ++recieveIndex )
			{
				g_request->additional[recieveIndex] = getchar();
			}
			//Verify checksum
			//Ignore for now
			processRequest();
		}

		//We must send a response
		sendResponse();
	}
#endif
}

